#include "Mesh.h"


Mesh::Mesh()
: device_(nullptr), mesh_(nullptr), meshMaterials_(nullptr), meshTextures_(nullptr), dwNumMaterials_(0)
{
	
}


// Destructor. Delete the resources.
Mesh::~Mesh()
{
    //delete all the materials then the array itself
	if (meshMaterials_ != nullptr)
	{
		delete[] meshMaterials_;
		meshMaterials_ = nullptr;
	}

    //delete all the textures
    if (meshTextures_ != nullptr)
    {
        //for each sub-mesh
		for (DWORD i = 0; i < dwNumMaterials_; ++i)
        {
            //release its texture
			if (meshTextures_[i])
				meshTextures_[i]->Release();
        }
        //then the array itself
		delete[] meshTextures_;
		meshTextures_ = nullptr;
    }

    //release the mesh
	if (mesh_ != nullptr)
	{
		mesh_->Release();
		mesh_ = nullptr;
	}
	if (XAnimator_ != nullptr)
	{
		XAnimator_ = nullptr;
	}
}

// Cleanup Routine. Just gets rid of everything then starts again.
void Mesh::Cleanup()
{
    //delete all the materials then the array itself
	if (meshMaterials_ != nullptr)
	{
		delete[] meshMaterials_;
		meshMaterials_ = nullptr;
	}

    //delete all the textures
    if (meshTextures_ != nullptr)
    {
        //for each sub-mesh
		for (DWORD i = 0; i < dwNumMaterials_; ++i)
        {
            //release its texture
			if (meshTextures_[i])
				meshTextures_[i]->Release();
        }
        //then the array itself
		delete[] meshTextures_;
		meshTextures_ = nullptr;
    }

    //release the mesh
	if (mesh_ != nullptr)
	{
		mesh_->Release();
		mesh_ = nullptr;
	}
}

// Load a mesh into this object using specified D3D device.
HRESULT Mesh::LoadMeshXA(IXAnimator *XAnim, LPDIRECT3DDEVICE9 device, LPCTSTR filename)
{
	// Precondition: requires a D3DDevice device
	assert( device != 0);

	// Set a device for this object
	device_ = device;
	XAnimator_ = XAnim;
	XAnimator_ -> LoadXFile(filename, &meshIdNumber_);
	TotalAnimSets_ = XAnimator_->GetNumberOfAnimationSets(meshIdNumber_);
	CurrentAnimSet_ = 0 ;
	nextAnimSet = 0;
	XAnimator_->ChangeAnimationSet(meshIdNumber_,CurrentAnimSet_,0.1f);
	return S_OK;
}

// Load a mesh into this object using specified D3D device.
HRESULT Mesh::LoadMesh(LPDIRECT3DDEVICE9 device, LPCTSTR filename)
{
	// Precondition: requires a D3DDevice device
	assert( device != 0);

	// Set a device for this object
	device_ = device;

	//local variable to temporarily hold data extracted from the .x file 
	LPD3DXBUFFER pD3DXMtrlBuffer; 

    // Load the mesh from the specified file
    if (FAILED(D3DXLoadMeshFromX(filename, D3DXMESH_SYSTEMMEM,
                                 device_,
								 NULL,
                                 &pD3DXMtrlBuffer,
								 NULL,
								 &dwNumMaterials_,
                                 &mesh_)))
    {
		MessageBox(NULL, "Cannot locate mesh file", "Mesh::LoadMesh", MB_OK);
        return E_FAIL;
    }

    // Extract the material properties and texture names from the pD3DXMtrlBuffer
    D3DXMATERIAL *d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer -> GetBufferPointer();
    // Create array to hold all the new textures and materials in memory.
	meshMaterials_ = new D3DMATERIAL9[dwNumMaterials_];
    meshTextures_  = new LPDIRECT3DTEXTURE9[dwNumMaterials_];

    //for each sub-mesh
	for (DWORD i( 0); i < dwNumMaterials_; ++i)
    {
        // Copy the material
        meshMaterials_[i] = d3dxMaterials[i].MatD3D;

        // Set the ambient color for the material (D3DX does not do this)
        meshMaterials_[i].Ambient = meshMaterials_[i].Diffuse;

        // If there is a filename for the texture for that part of the mesh
		meshTextures_[i] = nullptr;
        if (d3dxMaterials[i].pTextureFilename != NULL &&
            lstrlen(d3dxMaterials[i].pTextureFilename) > 0 )
        {
            // Create the texture
            if (FAILED(D3DXCreateTextureFromFile(device_,
                                                 d3dxMaterials[i].pTextureFilename,
                                                 &meshTextures_[i])))
				MessageBox(NULL, "Cannot locate texture map file", "Mesh::LoadMesh", MB_OK);
        }
    }

    // Done with the material buffer
    pD3DXMtrlBuffer -> Release();

    return S_OK;
}

void Mesh::Render() const
{
	// Precondition: requires a D3DDevice device
	assert( device_ != 0);

	 // Draw the mesh in sections - according to the different textures and materials.
	for (DWORD i( 0); i < dwNumMaterials_; ++i)
	{
		// Activate material and texture for that section.
		device_ -> SetMaterial(&(meshMaterials_[i]));
		device_ -> SetTexture(0, meshTextures_[i]);
		//Translations
		D3DXMATRIX TranslateMat;
		D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
		device_ -> SetTransform(D3DTS_WORLD, &TranslateMat);			// Reset the world to
		// Render the section that uses this material and texture.
		mesh_ -> DrawSubset(i);
		D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
		device_ -> SetTransform(D3DTS_WORLD, &TranslateMat);
	}
}

void Mesh::Render(float x, float y, float z, float rotY, float scaleX , float scaleY, float scaleZ) const
{
	// Precondition: requires a D3DDevice device
	assert( device_ != 0);
	//Translations
	D3DXMATRIX TranslateMat, ScaleMat, ObjMat, RotYMat;
	D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
	D3DXMatrixIdentity(&ScaleMat);
	D3DXMatrixIdentity(&RotYMat);
	D3DXMatrixIdentity(&ObjMat);

	D3DXMatrixRotationY(&RotYMat, rotY);
	D3DXMatrixTranslation(&TranslateMat, x, y, z);
	D3DXMatrixScaling(&ScaleMat,scaleX,scaleY,scaleZ);
	D3DXMatrixMultiply(&ObjMat,&RotYMat,&ScaleMat);
	D3DXMatrixMultiply(&ObjMat,&ObjMat,&TranslateMat);
	device_ -> SetTransform(D3DTS_WORLD, &ObjMat);					// Translate the world to

	// Draw the mesh in sections - according to the different textures and materials.

	for (DWORD i( 0); i < dwNumMaterials_; ++i)
	{
		// Activate material and texture for that section.
		device_ -> SetMaterial(&(meshMaterials_[i]));
		device_ -> SetTexture(0, meshTextures_[i]);
		// Render the section that uses this material and texture.
		mesh_ -> DrawSubset(i);
	}

	D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
	device_ -> SetTransform(D3DTS_WORLD, &TranslateMat);
}

void Mesh::RenderXA(float x, float y, float z, float rotY, float scaleX , float scaleY, float scaleZ)
{
	// Precondition: requires a D3DDevice device
	assert( device_ != 0);
	static DWORD lastUpdateTime=timeGetTime();
	//Translations
	D3DXMATRIX TranslateMat, ScaleMat, ObjMat, RotYMat;
	D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
	D3DXMatrixIdentity(&ScaleMat);
	D3DXMatrixIdentity(&RotYMat);
	D3DXMatrixIdentity(&ObjMat);

	D3DXMatrixRotationY(&RotYMat, rotY);
	D3DXMatrixTranslation(&TranslateMat, x, y, z);
	D3DXMatrixScaling(&ScaleMat,scaleX,scaleY,scaleZ);
	D3DXMatrixMultiply(&ObjMat,&RotYMat,&ScaleMat);
	D3DXMatrixMultiply(&ObjMat,&ObjMat,&TranslateMat);
	device_ -> SetTransform(D3DTS_WORLD, &ObjMat);					// Translate the world to

	// Draw the mesh in sections - according to the different textures and materials.
	DWORD timeElapsedSinceLastUpdate=timeGetTime()-lastUpdateTime;

	if(CurrentAnimSet_ != nextAnimSet)
	{
		CurrentAnimSet_ = nextAnimSet;
		XAnimator_->ChangeAnimationSet(meshIdNumber_,CurrentAnimSet_,0.01f);
	}

	XAnimator_ -> Render(meshIdNumber_, ObjMat, (float)timeElapsedSinceLastUpdate);
	lastUpdateTime=timeGetTime();

	D3DXMatrixIdentity(&TranslateMat);								// Set WorldMat to identity matrice
	device_ -> SetTransform(D3DTS_WORLD, &TranslateMat);
}